﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RaytracerLib.Layers;
using RaytracerLib.Resamplers;
using RaytracerLib.MathLib;
using RaytracerLib.Resamplers.Filters;

namespace RaytracerLib.Layers.Interpolators
{
    public class BicubicInterpolator : Interpolator
    {
        private BicubicFilter m_kernel = new BicubicFilter();

        public BicubicInterpolator(ArrayBase a_array)
            : base(a_array)
        {
        }

        public override ColorFloat InterpolateColor(double a_x, double a_y)
        {
            a_x = a_x + 0.5;
            int x = (int)a_x;
            a_y = a_y + 0.5;
            int y = (int)a_y;

            a_x = 1 - (a_x - x);
            a_y = 1 - (a_y - y);

            int x_minus_2 = OverlayCorrector.CorrectX(x - 2);
            int x_minus_1 = OverlayCorrector.CorrectX(x - 1);
            int x_plus_0 = OverlayCorrector.CorrectX(x + 0);
            int x_plus_1 = OverlayCorrector.CorrectX(x + 1);

            int y_minus_2 = OverlayCorrector.CorrectY(y - 2);
            int y_minus_1 = OverlayCorrector.CorrectY(y - 1);
            int y_plus_0 = OverlayCorrector.CorrectY(y + 0);
            int y_plus_1 = OverlayCorrector.CorrectY(y + 1);

            double fx_minus_2 = m_kernel.Evaluate(a_x - 2);
            double fx_minus_1 = m_kernel.Evaluate(a_x - 1);
            double fx_plus_0 = m_kernel.Evaluate(a_x + 0);
            double fx_plus_1 = m_kernel.Evaluate(a_x + 1);

            ColorFloat x1 = fx_minus_2 * m_array.GetColor(x_minus_2, y_minus_2) +
                            fx_minus_1 * m_array.GetColor(x_minus_1, y_minus_2) +
                            fx_plus_0 * m_array.GetColor(x_plus_0, y_minus_2) +
                            fx_plus_1 * m_array.GetColor(x_plus_1, y_minus_2);
            ColorFloat x2 = fx_minus_2 * m_array.GetColor(x_minus_2, y_minus_1) +
                            fx_minus_1 * m_array.GetColor(x_minus_1, y_minus_1) +
                            fx_plus_0 * m_array.GetColor(x_plus_0, y_minus_1) +
                            fx_plus_1 * m_array.GetColor(x_plus_1, y_minus_1);
            ColorFloat x3 = fx_minus_2 * m_array.GetColor(x_minus_2, y_plus_0) +
                            fx_minus_1 * m_array.GetColor(x_minus_1, y_plus_0) +
                            fx_plus_0 * m_array.GetColor(x_plus_0, y_plus_0) +
                            fx_plus_1 * m_array.GetColor(x_plus_1, y_plus_0);
            ColorFloat x4 = fx_minus_2 * m_array.GetColor(x_minus_2, y_plus_1) +
                            fx_minus_1 * m_array.GetColor(x_minus_1, y_plus_1) +
                            fx_plus_0 * m_array.GetColor(x_plus_0, y_plus_1) +
                            fx_plus_1 * m_array.GetColor(x_plus_1, y_plus_1);

            return x1 * m_kernel.Evaluate(a_y - 2) +
                   x2 * m_kernel.Evaluate(a_y - 1) +
                   x3 * m_kernel.Evaluate(a_y + 0) +
                   x4 * m_kernel.Evaluate(a_y + 1);
        }

        public override double InterpolateValue(double a_x, double a_y)
        {
            a_x = a_x + 0.5;
            int x = (int)a_x;
            a_y = a_y + 0.5;
            int y = (int)a_y;

            a_x = 1 - (a_x - x);
            a_y = 1 - (a_y - y);

            int x_minus_2 = OverlayCorrector.CorrectX(x - 2);
            int x_minus_1 = OverlayCorrector.CorrectX(x - 1);
            int x_plus_0 = OverlayCorrector.CorrectX(x + 0);
            int x_plus_1 = OverlayCorrector.CorrectX(x + 1);

            int y_minus_2 = OverlayCorrector.CorrectY(y - 2);
            int y_minus_1 = OverlayCorrector.CorrectY(y - 1);
            int y_plus_0 = OverlayCorrector.CorrectY(y + 0);
            int y_plus_1 = OverlayCorrector.CorrectY(y + 1);

            double fx_minus_2 = m_kernel.Evaluate(a_x - 2);
            double fx_minus_1 = m_kernel.Evaluate(a_x - 1);
            double fx_plus_0 = m_kernel.Evaluate(a_x + 0);
            double fx_plus_1 = m_kernel.Evaluate(a_x + 1);

            double x1 = fx_minus_2 * m_array.GetValue(x_minus_2, y_minus_2) +
                        fx_minus_1 * m_array.GetValue(x_minus_1, y_minus_2) +
                        fx_plus_0 * m_array.GetValue(x_plus_0, y_minus_2) +
                        fx_plus_1 * m_array.GetValue(x_plus_1, y_minus_2);
            double x2 = fx_minus_2 * m_array.GetValue(x_minus_2, y_minus_1) +
                        fx_minus_1 * m_array.GetValue(x_minus_1, y_minus_1) +
                        fx_plus_0 * m_array.GetValue(x_plus_0, y_minus_1) +
                        fx_plus_1 * m_array.GetValue(x_plus_1, y_minus_1);
            double x3 = fx_minus_2 * m_array.GetValue(x_minus_2, y_plus_0) +
                        fx_minus_1 * m_array.GetValue(x_minus_1, y_plus_0) +
                        fx_plus_0 * m_array.GetValue(x_plus_0, y_plus_0) +
                        fx_plus_1 * m_array.GetValue(x_plus_1, y_plus_0);
            double x4 = fx_minus_2 * m_array.GetValue(x_minus_2, y_plus_1) +
                       fx_minus_1 * m_array.GetValue(x_minus_1, y_plus_1) +
                       fx_plus_0 * m_array.GetValue(x_plus_0, y_plus_1) +
                       fx_plus_1 * m_array.GetValue(x_plus_1, y_plus_1);

            return x1 * m_kernel.Evaluate(a_y - 2) +
                   x2 * m_kernel.Evaluate(a_y - 1) +
                   x3 * m_kernel.Evaluate(a_y + 0) +
                   x4 * m_kernel.Evaluate(a_y + 1);
        }
    }
}
